﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace EssentialDialog
{
    public partial class FrmLongLoading : Form
    {

        Form parentForm = null;

        [DllImport("USER32.DLL")]
        public static extern int GetSystemMenu(int hwnd, int bRevert);
        [DllImport("USER32.DLL")]
        public static extern int EnableMenuItem(int hMenu, int nPosition, int wFlags);

        private const int MF_GRAYED = 0x1;
        private const int MF_ENABLED = 0x0;

        const int SC_CLOSE = 0xF060; 


        private FrmLongLoading(Form parentForm,Action<ProgressStateAccessor> work)
        {
            InitializeComponent();
            ShowInTaskbar = false;
            this.parentForm = parentForm;
            this.work = work;
        }

        Graphics progressBarGraphics = null;
        Bitmap bufferedProgressBarImage = null;
        Graphics g = null;
        Brush whiteBrush = Brushes.White;
        Pen intermedatePen = new Pen(Brushes.White, 5f);

        private void FrmLongLoading_Load(object sender, EventArgs e)
        {
            SyncWindowPos();
            parentForm.SizeChanged += ParentForm_SizeChanged;
            parentForm.Move += ParentForm_Move;
            progressBarGraphics = Graphics.FromHwnd(tblProgressBar.Handle);
            progressBarGraphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
            bufferedProgressBarImage = new Bitmap(tblProgressBar.Width, tblProgressBar.Height);
            g = Graphics.FromImage(bufferedProgressBarImage);
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            animTimer.Start();
            backgroundWorker1.RunWorkerAsync();
        }

        private void ParentForm_Move(object sender, EventArgs e)
        {
            SyncWindowPos();
        }

        private void ParentForm_SizeChanged(object sender, EventArgs e)
        {
            SyncWindowPos();
        }

        private void SyncWindowPos()
        {
            if(parentForm.WindowState == FormWindowState.Minimized)
            {
                this.Visible = false;
            }
            else
            {
                this.Visible = true;
            }
            this.Width = parentForm.ClientSize.Width;
            this.Left = parentForm.PointToScreen(Point.Empty).X;
            this.Top = parentForm.PointToScreen(Point.Empty).Y + parentForm.ClientSize.Height / 2 - this.Height / 2;
            
        }

        private bool isIndetermine = true;
        private int begin = 0;
        private int end = 100;
        private int current = 0;
        private int total = 1;
        private string text = "请稍后...";
        private float t = 0;



        private void animTimer_Tick(object sender, EventArgs e)
        {
            bool isIndetermine = true;
            int begin = 0;
            int end = 100;
            int current = 0;
            int total = 1;
            string text = "请稍后...";

            lock (this)
            {
                isIndetermine = this.isIndetermine;
                begin = this.begin;
                end = this.end;
                current = this.current;
                total = this.total;
                text = this.text;

            }

            g.Clear(this.BackColor);

            if (isIndetermine)
            {
                t += 0.4f;
                for (float i = 0; i < tblProgressBar.Width + 15; i += 10)
                {
                    g.DrawLine(intermedatePen, i - t, -3, i - 8f - t, 20);
                }
                if (t > 10)
                {
                    t -= 10;
                }
            }
            else
            {
                float progressFactor = clamp(current / (total == 0f ? 1f : total));

                float baseBegin = begin / 100f;
                float baseLen = (end - begin) / 100f;
                float actualWidth = baseBegin + baseLen * progressFactor;

                float width = actualWidth * (float)tblProgressBar.Width;
                g.FillRectangle(whiteBrush, 0f, 0f, width, 20f);
            }
            lblMsg.Text = text;

            progressBarGraphics.DrawImage(bufferedProgressBarImage, 0, 0, bufferedProgressBarImage.Width, bufferedProgressBarImage.Height);
        }

        private float clamp(float f) { if (f > 1) { return 1; } if (f < 0) { return 0; } return f; }

        public class ProgressStateAccessor
        {
            private FrmLongLoading _accessor;

            public ProgressStateAccessor(FrmLongLoading accessor)
            {
                _accessor = accessor;

            }

            public bool isIndetermine = true;
            public int begin = 0;
            public int end = 100;
            public int current = 0;
            public int total = 1;
            public string text = "请稍后...";

            public void syncProgress()
            {
                lock (_accessor)
                {
                    _accessor.isIndetermine = isIndetermine;
                    _accessor.begin = begin;
                    _accessor.end = end;
                    _accessor.current = current;
                    _accessor.total = total;
                    _accessor.text = text;
                }
            }

            public void SetSegement(int begin, int end)
            {
                this.begin = begin;
                this.end = end;
                this.current = 0;
                this.isIndetermine = false;
                syncProgress();
            }

            public void SetMessage(string msg)
            {
                this.text = msg;
                syncProgress();
            }

            public void SetProgress(int current,int total)
            {
                this.current = current;
                this.total = total;
                this.isIndetermine = false;
                syncProgress();
            }

            public void SetIndetermine(bool indetermine)
            {
                this.isIndetermine = indetermine;
                syncProgress();
            }
        }

        private Action<ProgressStateAccessor> work;
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                work(new ProgressStateAccessor(this));
            }catch(Exception ex)
            {
                e.Result = ex;
            }
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {

        }

        private Exception err = null;

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            err = e.Result as Exception;
            animTimer.Enabled = false;
            Close();
        }
        private void FrmLongLoading_FormClosed(object sender, FormClosedEventArgs e)
        {
            g.Dispose();
            progressBarGraphics.Dispose();
            bufferedProgressBarImage.Dispose();
            parentForm.Move -= ParentForm_Move;
            parentForm.SizeChanged -= ParentForm_SizeChanged;
        }

        private void DisableCloseButton(int handle)
        {
            int hMenu = GetSystemMenu(handle, 0);
            EnableMenuItem(hMenu, SC_CLOSE, MF_GRAYED);
        }

        private void EnableCloseButton(int handle)
        {
            int hMenu = GetSystemMenu(handle, 0);
            EnableMenuItem(hMenu, SC_CLOSE, MF_ENABLED);
        }

        public static void RunWork(Form attachForm,Action<ProgressStateAccessor> work,Action<Exception> callback = null)
        {
            FrmLongLoading form = new FrmLongLoading(attachForm,work);
            
            form.Show(attachForm);

            form.DisableCloseButton(attachForm.Handle.ToInt32());
            
                form.FormClosed += delegate
                {
                    form.EnableCloseButton(attachForm.Handle.ToInt32()) ;   
                    if(callback != null)
                    {
                        attachForm.BeginInvoke(callback,(form.err));
                        callback = null;
                    }
                    
                };
            
        }

        
    }
}
